package joe.works.commons.beans;

import java.lang.reflect.Method;
import java.util.Map;

public class BeanPropertyReader<BeanType, PropertyType> {
	private BeanType bean;
	private String property;
	
	public void setBean(BeanType bean) {
		this.bean = bean;
	}
	
	public void setProperty(String property) {
		this.property = property;
	}
	
	@SuppressWarnings("unchecked")
	public PropertyType readProperty() {
		if(bean == null) {
			return null;
		}
		if(bean instanceof Map) {
			return (PropertyType) ((Map)bean).get(property);
		}
		Method getter = getGetter((Class<BeanType>) bean.getClass(), property);
		if(getter == null) {
			throw new IllegalArgumentException("Unable to read property " + property + " in bean " + bean + " : " + bean.getClass());
		}
		return (PropertyType) invokeMethodSilently(getter, bean);
	}
	
	private Method getGetter(Class<BeanType> clazz, String property) {
		String methodPart = "" + Character.toUpperCase(property.charAt(0)) + property.substring(1);
		String[] possibleMethodNames = {
			"get"+methodPart,
			"is"+methodPart
		};
		for (int i = 0; i < possibleMethodNames.length; i++) {
			Method methodSilently = getMethodSilently(clazz, possibleMethodNames[i]);
			if(methodSilently != null) {
				return methodSilently;
			}
		}
		return null;
	}
	
	private Method getMethodSilently(Class<BeanType> clazz, String getterMethod) {
		try {
			return clazz.getMethod(getterMethod, new Class[0]);
		} catch (NoSuchMethodException e) {
			//ignore
		}
		return null;
	}
	
	private Object invokeMethodSilently(Method getterMethod, Object bean) {
		try {
			return getterMethod.invoke(bean, new Object[0]);
		} catch (Exception ex) {
			throw new IllegalArgumentException("Unable to read property " + property + " in bean " + bean + " : " + bean.getClass());
		}
	}

}
